package org.luxor.commons.redis.component;

import org.aspectj.lang.ProceedingJoinPoint;
import org.luxor.commons.core.exception.BaseException;
import org.luxor.commons.core.web.RestStatus;
import org.luxor.commons.redis.annotation.*;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 分布式锁Key生成器
 *
 * @author zengzh
 * @since 1.0.0
 */
public class KeyGenerator {
    public static final KeyGenerator INSTANCE = new KeyGenerator();

    private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();

    private static final ExpressionParser PARSER = new SpelExpressionParser();

    /**
     * 获取@Cacheable缓存Key
     */
    public String getCacheKeyName(ProceedingJoinPoint pjp, RedisCachePut redisCachePut) {
        // 获取注解所在类、方法、参数信息
        Class<?> localeClass = pjp.getTarget().getClass();
        Method localeMethod = ReflectUtil.getMethod(pjp);
        Object[] arguments = pjp.getArgs();

        String[] definitionKeys = redisCachePut.keys();
        StringBuilder sb = new StringBuilder();
        if (StringUtils.hasText(redisCachePut.cacheNames())) {
            sb.append(redisCachePut.cacheNames());
        } else {
            sb.append(localeClass.getClass().getName());
        }
        if (definitionKeys.length > 0) {
            sb.append("#").append(getSpelDefinitionKey(definitionKeys, localeMethod, arguments));
        }
        return sb.toString();
    }

    /**
     * 获取@Cacheable缓存Key
     */
    public String getCacheKeyName(ProceedingJoinPoint pjp, RedisCacheable redisCacheable) {
        // 获取注解所在类、方法、参数信息
        Class<?> localeClass = pjp.getTarget().getClass();
        Method localeMethod = ReflectUtil.getMethod(pjp);
        Object[] arguments = pjp.getArgs();

        String[] definitionKeys = redisCacheable.keys();
        StringBuilder sb = new StringBuilder();
        if (StringUtils.hasText(redisCacheable.cacheNames())) {
            sb.append(redisCacheable.cacheNames());
        } else {
            sb.append(localeClass.getClass().getName());
        }
        if (definitionKeys.length > 0) {
            sb.append("#").append(getSpelDefinitionKey(definitionKeys, localeMethod, arguments));
        }
        return sb.toString();
    }

    /**
     * 获取@Cacheable缓存Key
     */
    public String getCacheEvictKeyName(ProceedingJoinPoint pjp, RedisCacheEvict redisCacheEvict) {
        // 获取注解所在类、方法、参数信息
        Class<?> localeClass = pjp.getTarget().getClass();
        Method localeMethod = ReflectUtil.getMethod(pjp);
        Object[] arguments = pjp.getArgs();

        String[] definitionKeys = redisCacheEvict.keys();
        StringBuilder sb = new StringBuilder();
        if (StringUtils.hasText(redisCacheEvict.cacheNames())) {
            sb.append(redisCacheEvict.cacheNames());
        } else {
            sb.append(localeClass.getClass().getName());
        }
        if (definitionKeys.length > 0) {
            sb.append("#").append(getSpelDefinitionKey(definitionKeys, localeMethod, arguments));
        }
        return sb.toString();
    }

    /**
     * 获取@LimitRate注解key
     */
    public String getlimitRateKey(ProceedingJoinPoint pjp, LimitRate limitRate) {
        // 获取注解所在类、方法、参数信息
        Class<?> localeClass = pjp.getTarget().getClass();
        Method localeMethod = ReflectUtil.getMethod(pjp);
        Object[] arguments = pjp.getArgs();

        StringBuilder sb = new StringBuilder(localeClass.getName()).append(".").append(localeMethod.getName());
        if (!StringUtils.isEmpty(limitRate.key())) {
            String value = getSpelDefinitionKey(new String[]{limitRate.key()}, localeMethod, arguments);
            sb.append("#").append(value);
        }
        return sb.toString();
    }


    /**
     * 获取@Idempotent注解key
     */
    public String getIdempotentToken(ProceedingJoinPoint pjp, Idempotent idempotent) throws BaseException {
        // 获取注解所在类、方法、参数信息
        Method localeMethod = ReflectUtil.getMethod(pjp);
        Object[] arguments = pjp.getArgs();

        String tokenVal = null;
        if (!StringUtils.isEmpty(idempotent.token())) {
            tokenVal = getSpelDefinitionKey(new String[]{idempotent.token()}, localeMethod, arguments);
        }
        if (org.luxor.commons.core.utils.StringUtils.isBlank(tokenVal)) {
            throw new BaseException(RestStatus.PARAM_ILLEGAL_FIELDS, "接口幂等验性证失败,Token值为空！");
        }
        return tokenVal;
    }

    private String getSpelDefinitionKey(String[] definitionKeys, Method method, Object[] arguments) {
        EvaluationContext context = new MethodBasedEvaluationContext(null, method, arguments, NAME_DISCOVERER);
        List<String> definitionKeyList = new ArrayList<>(definitionKeys.length);
        for (String definitionKey : definitionKeys) {
            if (definitionKey != null && !definitionKey.isEmpty()) {
                if (definitionKey.startsWith("#")) {
                    String key = PARSER.parseExpression(definitionKey).getValue(context).toString();
                    definitionKeyList.add(key);
                } else {
                    definitionKeyList.add(definitionKey);
                }
            }
        }
        return StringUtils.collectionToDelimitedString(definitionKeyList, "_", "", "");
    }


}
